﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
using UnityEngine.Networking;

namespace Z1029.NoteEditor
{
    public class LevelController : MonoBehaviour
    {
        [Header("Edit")]
        public bool Play;
        public float NowTime;
        public int NowBeatTime;
        public float TimeScale;
        [Header("Init Settings")]

        public AudioClip music;
        public Camera viewCam;

        public EventLine eventEdit;

        public CreaterCtrl createrCtrl;

        public Sprite illustrationBlur;
        public Transform m_level;
        public string musicName, levelInfo, levelHarder;
        public SpriteRenderer background;
        public GameObject judgelinePrefab;
        public JudgementLine[] judgelines;
        public NotesManager notesManager;
        public HitEffectManager hitEffectManager;
        public MusicPlayerCtrl musicCtrl;
        public int notesCount;
        public static LevelController instance;
        private float musicLength;
        public AudioSource musicSource;
        public static double nowTime;
        public const int density = 32;
        public Note[] notes;
        public int nowDensity = 32;
        public float tracklineDensity = 2;
        public bool showBeatline;
        public bool showTrackLine;
        public bool multiSelectNote;
        public float bpm;
        public EditMode editMode;

        bool successLoadMusic;

        public static float noteSize = 0.26f;
        public static float hitSoundsVolume = 0.6f;
        public float offset;

        public enum EditMode
        {
            Note, Event
        }

        private void Awake()
        {
            int val = PlayerPrefs.GetInt("DspBuffer", 512);
            AudioSettings.SetDSPBufferSize(val, val);

            InitEditor();

            Z1029.NoteGlobalView.LevelController.instance.Play = false;
            Z1029.NoteGlobalView.JudgementLine.IsEditing = true;
            Application.targetFrameRate = 90;
            LevelController.instance = this;

            if (Screen.width < 1920 || Screen.height < 1080)
            {
                foreach (Z1029WindowControl w in FindObjectsOfType<Z1029WindowControl>())
                {
                    ((RectTransform)w.gameObject.transform).anchoredPosition = Vector2.zero;
                    ((RectTransform)w.gameObject.transform).sizeDelta /= 1.25f;
                }
            }
            Screen.SetResolution(1920, 1080, true);
            string path = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Project//music.wav");
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            byte[] bytes = new byte[fs.Length];
            fs.Read(bytes, 0, (int)fs.Length);
            fs.Close();

            successLoadMusic = false;
            music = Z1029.NoteGlobalView.LevelController.instance.music;
            musicCtrl.gameObject.SetActive(false);
            StartCoroutine(LoadMusic(path));
            path = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Project//Chart.json");
            fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            bytes = new byte[fs.Length];
            fs.Read(bytes, 0, (int)fs.Length);
            fs.Close();
            NowBeatTime = NoteGlobalView.LevelController.instance.NowBeatTime;
        }

        public void InitEditor()
        {
            bpm = NoteGlobalView.JudgementLine.selectedJudgeline.bpm;
            if (bpm == 0) throw new Exception("BPM=0");
            this.notes = new Note[9999];
            offset = NoteGlobalView.LevelController.instance.gameChart.offset;
            NoteGlobalView.JudgementLine jss = NoteGlobalView.JudgementLine.selectedJudgeline;
            JudgeLineSet js = new JudgeLineSet();
            js.alphas = jss.alphaEvents;
            js.moves = jss.moveEvents;
            js.rotates = jss.rotateEvents;
            NoteGlobalView.Note[] notes = NoteGlobalView.JudgementLine.selectedJudgeline.GetComponentsInChildren<NoteGlobalView.Note>();
            js.speedEvents = new List<SpeedEvent>();
            for (int i = 0; i < jss.speeds.Length; i++)
            {
                SpeedEvent sp = new SpeedEvent() { startTime = jss.speeds[i].startTime, endTime = jss.speeds[i].endTime, value = jss.speeds[i].value };
                js.speedEvents.Add(sp);
            }
            Transform lineObj = Instantiate(judgelinePrefab.transform);
            judgelines[0] = lineObj.GetComponent<JudgementLine>();
            lineObj.parent = m_level;
            JudgementLine line = judgelines[0];
            line.bpm = bpm;
            line.alphas = js.alphas;
            line.moves = js.moves;
            line.rotates = js.rotates;
            line.speeds = js.speedEvents;

            for (int i = 0; i < notes.Length; i++)
            {
                notesManager.InstantiateNote(notes[i], judgelines[0]);
            }

            
        }

        public IEnumerator LoadMusic(string path)
        {

            //path = Application.dataPath + "Project//music.ogg";
            path = "file:///" + path;
            WWW www = new WWW(path);
            yield return www;
            if (www.error == null)
            {
                music = www.GetAudioClip();
                musicLength = music.length;
                musicSource.clip = music;
                Z1029.NoteGlobalView.LevelController.instance.musicSource.clip = music;
                Z1029.NoteGlobalView.LevelController.instance.music = music;
                print("LoadMusicSuccess");
                NowBeatTime = NoteGlobalView.LevelController.instance.NowBeatTime;
                musicCtrl.gameObject.SetActive(true);
                successLoadMusic = true;
            }
            else
            {
                Debug.LogError(www.error);
                successLoadMusic = true;
            }

        }

        public Sprite GetSprite(string path)
        {
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            byte[] bytes = new byte[fs.Length];
            fs.Read(bytes, 0, (int)fs.Length);
            int width = 512;
            int height = 270;
            Texture2D t2d = new Texture2D(width, height);
            t2d.LoadImage(bytes);
            Sprite sp = Sprite.Create(t2d, new Rect(0, 0, t2d.width, t2d.height), new Vector2(0.5f, 0.5f));
            return sp;
        }

        public float camDistance
        {
            get
            {
                return -viewCam.transform.position.z;
            }
        }

        private void Start()
        {
        }

        public void Quit()
        {
            Z1029.NoteGlobalView.JudgementLine.IsEditing = false;
            Z1029.NoteGlobalView.LevelController.nowTime = LevelController.nowTime;
            Z1029.NoteGlobalView.LevelController.instance.musicSource.pitch = Z1029.NoteGlobalView.LevelController.instance.TimeScale;
            UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(1);
        }

        private void Update()
        {
            if (true)
            {
                Z1029.NoteGlobalView.LevelController.instance.Play = Play;
                noteSize = (viewCam.orthographicSize * 0.26f / 5 - 0.26f) * 0.3f + 0.26f;
                if (Input.GetKeyDown(KeyCode.Space))
                {
                    Play = !Play;
                    musicCtrl.PlayPause();
                }
                if (Play)
                {
                    nowTime += Time.smoothDeltaTime * TimeScale;
                    musicSource.pitch = TimeScale;
                    NowTime = (float)nowTime;
                    NowBeatTime = Utils.GetBeatTimeFromTime(nowTime, judgelines[0].bpm);
                    if (!musicSource.isPlaying)
                    {
                        musicSource.time = NowTime;
                        musicSource.Play();
                    }
                }
                else
                {
                    nowTime = Utils.GetTimeFromBeatTime(NowBeatTime, judgelines[0].bpm);
                    musicSource.time = (float)nowTime;
                    if (musicSource.isPlaying && !musicCtrl.changing) musicSource.Pause();
                }
                if (nowTime + 0.1f > musicLength)
                {
                    nowTime = musicLength - 0.1f;
                    Play = false;
                }

                if (Input.GetKeyDown(KeyCode.LeftControl)) multiSelectNote = true;
                if (Input.GetKeyUp(KeyCode.LeftControl)) multiSelectNote = false;
            }
            if (Z1029.NoteGlobalView.LevelController.instance) Z1029.NoteGlobalView.LevelController.instance.NowBeatTime = NowBeatTime;
        }

        public void SetReload()
        {

        }

        public void ChangeMode()
        {
            if (editMode == EditMode.Note)
            {
                judgelines[0].gameObject.SetActive(true);
                eventEdit.gameObject.SetActive(false);
                FindObjectOfType<NoteSetter>().enabled = true;
                createrCtrl.gameObject.active = true;
            }
            else if (editMode == EditMode.Event)
            {
                judgelines[0].gameObject.SetActive(false);
                eventEdit.gameObject.SetActive(true);
                FindObjectOfType<NoteSetter>().enabled = false;
                createrCtrl.gameObject.active = false;
            }
        }
    }
    public class WAV
    {

        // convert two bytes to one float in the range -1 to 1
        static float bytesToFloat(byte firstByte, byte secondByte)
        {
            // convert two bytes to one short (little endian)
            short s = (short)((secondByte << 8) | firstByte);
            // convert to range from -1 to (just below) 1
            return s / 32768.0F;
        }

        static int bytesToInt(byte[] bytes, int offset = 0)
        {
            int value = 0;
            for (int i = 0; i < 4; i++)
            {
                value |= ((int)bytes[offset + i]) << (i * 8);
            }
            return value;
        }

        private static byte[] GetBytes(string filename)
        {
            return File.ReadAllBytes(filename);
        }
        // properties
        public float[] LeftChannel { get; internal set; }
        public float[] RightChannel { get; internal set; }
        public int ChannelCount { get; internal set; }
        public int SampleCount { get; internal set; }
        public int Frequency { get; internal set; }

        // Returns left and right double arrays. 'right' will be null if sound is mono.
        public WAV(string filename) :
            this(GetBytes(filename))
        { }

        public WAV(byte[] wav)
        {

            // Determine if mono or stereo
            ChannelCount = wav[22];     // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels

            // Get the frequency
            Frequency = bytesToInt(wav, 24);

            // Get past all the other sub chunks to get to the data subchunk:
            int pos = 12;   // First Subchunk ID from 12 to 16

            // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))
            while (!(wav[pos] == 100 && wav[pos + 1] == 97 && wav[pos + 2] == 116 && wav[pos + 3] == 97))
            {
                pos += 4;
                int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
                pos += 4 + chunkSize;
            }
            pos += 8;

            // Pos is now positioned to start of actual sound data.
            SampleCount = (wav.Length - pos) / 2;     // 2 bytes per sample (16 bit sound mono)
            if (ChannelCount == 2) SampleCount /= 2;        // 4 bytes per sample (16 bit stereo)

            // Allocate memory (right will be null if only mono sound)
            LeftChannel = new float[SampleCount];
            if (ChannelCount == 2) RightChannel = new float[SampleCount];
            else RightChannel = null;

            // Write to double array/s:
            int i = 0;
            while (pos < wav.Length)
            {
                LeftChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
                pos += 2;
                if (ChannelCount == 2)
                {
                    RightChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);
                    pos += 2;
                }
                i++;
            }
        }

        public override string ToString()
        {
            return string.Format("[WAV: LeftChannel={0}, RightChannel={1}, ChannelCount={2}, SampleCount={3}, Frequency={4}]", LeftChannel, RightChannel, ChannelCount, SampleCount, Frequency);
        }
    }
}
